﻿#region References

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using App_Code.Common;
using App_Code.Logic;
using VisualCron;
using VisualCronAPI;

#endregion

namespace App_Code.Objects
{
    /// <summary>
    /// Class to hold all data for single user session
    /// </summary>
    public class VisualCronSession
    {
        #region Private fields

        private Client _client;
        private List<Server> _servers;
        private List<VCConnection> _connections;
        private Dictionary<string, ManualResetEvent> _serversSycnrhonized = new Dictionary<string, ManualResetEvent>();

        #endregion

        #region Properties

        public Client Client
        {
            get
            {
                if (_client == null)
                {
                    _client = new Client();

                    _client.EventOnConnected += new VisualCronAPI.Client.DelegateOnConnected(Client_EventOnConnected);
                    _client.EventOnDisconnect += new VisualCronAPI.Client.DelegateOnDisconnect(Client_EventOnDisconnect);
                }
                return _client;
            }
            set { _client = value; }
        }

        public List<Server> Servers
        {
            get { return _servers ?? (_servers = new List<Server>()); }
            set { _servers = value; }
        }

        public List<VCConnection> Connections
        {
            get
            {
                if (_connections == null || _connections.Count == 0)
                {
                    _connections = ConnectionLogic.GetNewConnectionsList();
                }
                return _connections;
            }
            set { _connections = value; }
        }

        public string AspNetSessionId { get; set; }

        public string SignalRClientId { get; set; }

        public List<ServerDTO> ServersDTO
        {
            get
            {
                var serversDTO = new List<ServerDTO>(Servers.Count);
                serversDTO.AddRange(Servers.Select(server => new ServerDTO(server) ));

                return serversDTO;
            }
        }

        public UserProfile Profile { get; set; }
        #endregion

        #region Event Handlers

        private void Client_EventOnConnected(Client c, ref Server s)
        {
            var serverId = s.UserServerId;

            // we only add unique server connections
            if (this.Servers.Exists(server => server.UserServerId == serverId))
            {
                return;
            }
            this.Servers.Add(s);

            SignalRLogic.SendServerConnected(this.SignalRClientId, new ServerDTO(s));
        }

        public void Client_EventOnDisconnect(Client c, ref Server s, bool bolIsException, string strError)
        {
            string serverId = s.UserServerId;

            this.Servers.RemoveAll(x => x.UserServerId == serverId);

            SignalRLogic.SendServerDisconnected(SignalRClientId, serverId, strError);
        }

        public void Job_Added(string serverId, VisualCron.JobClass job)
        {
            SignalRLogic.SendJobAdded(SignalRClientId, serverId, new JobDTO(serverId, job));
        }

        public void Job_Removed(string serverId, JobClass job)
        {
            SignalRLogic.SendJobRemoved(SignalRClientId, serverId, new JobDTO(serverId, job));
        }

        public void Job_Updated(string userServerId, JobClass job)
        {
            SignalRLogic.SendJobUpdated(SignalRClientId, userServerId, new JobDTO(userServerId, job));
        }

        public void Task_Added(string serverId, string jobId, TaskClass task, bool isTakRunning)
        {
            SignalRLogic.SendTaskAdded(SignalRClientId, serverId, jobId, new TaskDTO(task, isTakRunning));
        }

        public void Task_Removed(string serverId, string jobId, TaskClass task, bool isTakRunning)
        {
            SignalRLogic.SendTaskRemoved(SignalRClientId, serverId, jobId, new TaskDTO(task, isTakRunning));
        }

        public void Task_Updated(string serverId, string jobId, TaskClass task, bool isTakRunning)
        {
            SignalRLogic.SendTaskUpdated(SignalRClientId, serverId, jobId, new TaskDTO(task, isTakRunning));
        }
        
        public void ConnectionStateChanged()
        {
            SignalRLogic.SendConnectionStateChanged(SignalRClientId, Connections);
        }

        public void Connection_Error(string connectionId, bool isLoginError, string message)
        {
            SignalRLogic.SendConnectionError(SignalRClientId, connectionId, isLoginError, message);
        }
        #endregion

        #region Public methods

        public ManualResetEvent GetServerSynchronized(string userServerId)
        {
            ManualResetEvent res = null;
            lock (_serversSycnrhonized)
            {
                if (_serversSycnrhonized.ContainsKey(userServerId))
                {
                    res = _serversSycnrhonized[userServerId];
                }
                else
                {
                    res = _serversSycnrhonized[userServerId] = new ManualResetEvent(false);
                }
            }
            return res;
        }

        public List<JobDTO> GetJobsDTO(string userServerId)
        {
            var result = new List<JobDTO>();

            var server = this.Servers.Find(x => x.UserServerId == userServerId);

            if (server != null)
            {
                // we wait for server synchronization event, because otherwise Jobs.GetAll returns 0 records
                GetServerSynchronized(userServerId).WaitOne(Constants.SERVER_SYNC_WAIT_TIMEOUT_MS);
                var jobs = server.Jobs.GetAll();

                result.AddRange(jobs.Select(job => new JobDTO(server.UserServerId, job)));
            }

            return result;
        }

        public List<TaskDTO> GetTasksDTO(string serverId, string jobId)
        {
            var result = new List<TaskDTO>();

            var server = this.Servers.Find(x => x.UserServerId == serverId);

            if (server != null)
            {
                var job = server.Jobs.Get(jobId);

                if (job != null)
                {
                    result.AddRange(job.Tasks.Select(task => new TaskDTO(task, VisualCronLogic.IsTaskRunning(server, task))));
                }
            }

            return result;
        }
        #endregion

    }
}